package engine;

public class ChessBoard implements Defs {

	// ***
	// Variables used to describe the position
	// ***
	
	private int[] boardArray; 		// Represents the 0x88 board

	private int toMove; 			// Side to move on the board

	private int enPassant;  		// Index on boardArray where en passant is
						// available, -1 if none

	private int white_castle;		// White's ability to castle
	private int black_castle; 		// Black's ability to castle

	private int movesFifty;			// Keeps track of half-moves since last capture
						// or pawn move
						
	private int movesFull; 			// Keeps track of total full moves in the game

	//END Variables

	/**
	 *  Creates an empty board
	 *
	 *  @return An empty Board
	 */
	public Board()
	{
		this.boardArray = new int[128];
		this.toMove = 1; // White to move
		this.enPassant = -1; // No en passants available
		this.white_castle = CASTLE_NONE;
		this.black_castle = CASTLE_NONE;
	}
	// END Board()

	/**
	 *  Makes a Move on the board
	 *
	 *  @param Move move
	 */
	public void makeMove(Move move)
	{
		enPassant = -1; // Set the en passant square to none, will be reset below if it becomes available

		toMove *= -1; // Switch side to move (multiply toMove by -1 simply switches from negative to positive
		// and vice versa)

		if(move.pieceMoving != W_PAWN && move.pieceMoving != B_PAWN && move.capture == 0)
			movesFifty++; // Increment the moves fifty if not a pawn moving or a capture
		else
			movesFifty = 0; // If a pawned moved or it was a capture, reset the movesFifty count

		if(move.pieceMoving < 0)
			movesFull++; // Increment the total of full moves if the moving piece is black

		// Now find out what kind of move it is and act accordingly
		switch(move.moveType)
		{
			case ORDINARY_MOVE:
			{
				boardArray[move.toIndex] = move.pieceMoving; // Set target square
				boardArray[move.fromIndex] = EMPTY_SQUARE; // Clear the original square

				// Check for en passant
				// If the piece moving is a white or black pawn, and it has moved
				// 2 squares (the Math.abs line checks that)
				// set the en passant square to the right index and exit the move
				if((move.pieceMoving == W_PAWN || move.pieceMoving == B_PAWN)
						&& Math.abs(move.toIndex - move.fromIndex) == 32)
				{
					enPassant = move.fromIndex + (move.toIndex - move.fromIndex)/2;
					break;
				}
				break;
			}

			case SHORT_CASTLE:
			{
				if (move.pieceMoving == W_KING) // White king castles short
				{
					boardArray[7] = EMPTY_SQUARE; // Empty the rook square
					boardArray[4] = EMPTY_SQUARE; // Empty the king square
					boardArray[6] = W_KING; // Put white king
					boardArray[5] = W_ROOK; // Put white rook
					white_castle = CASTLE_NONE; // Make further castling impossible for white
				}
				if (move.pieceMoving == B_KING) // Black king castles short
				{
					boardArray[119] = EMPTY_SQUARE; // Empty the rook square
					boardArray[116] = EMPTY_SQUARE; // Empty the king square
					boardArray[118] = B_KING; // Put black king
					boardArray[117] = B_ROOK; // Put black rook
					black_castle = CASTLE_NONE; // Make further castling impossible for black
				}				
				break;
			}

			case LONG_CASTLE:
			{
				if (move.pieceMoving == W_KING) // White king castles long
				{
					boardArray[0] = EMPTY_SQUARE; // Empty the rook square
					boardArray[4] = EMPTY_SQUARE; // Empty the king square
					boardArray[2] = W_KING; // Put white king
					boardArray[3] = W_ROOK; // Put white rook
					white_castle = CASTLE_NONE; // Make further castling impossible for white
				}
				if (move.pieceMoving == B_KING) // Black king castles lon
				{
					boardArray[112] = EMPTY_SQUARE; // Empty the rook square
					boardArray[116] = EMPTY_SQUARE; // Empty the king square
					boardArray[114] = B_KING; // Put black king
					boardArray[115] = B_ROOK; // Put black rook
					black_castle = CASTLE_NONE; // Make further castling impossible for black
				}		
				break;
			}

			case EN_PASSANT:
			{
				boardArray[move.toIndex] = move.pieceMoving; // Set target square
				boardArray[move.fromIndex] = EMPTY_SQUARE; // Clear the original square

				// Since it's an en passant capture we also need to remove the captured pawn
				// which resides one square up/down from the target square
				// If it's a white pawn capturing, clear the square below it
				if(move.pieceMoving == W_PAWN) boardArray[move.toIndex - 16] = EMPTY_SQUARE;
				// If it wasn't a white pawn, it's a black pawn and we clear the square above it
				else boardArray[move.toIndex + 16] = EMPTY_SQUARE;				

				break;
			}

			default: // Catches promotions since that's the only type of move left
			{
				// Check for queen, rook, bishop and knight promotions
				switch(move.moveType)
				{
					// Put a promoted piece of the right color on the target square
					// We use the toMove property to get the right color but since
					// we already changed side to move we need the opposite color
					// 
					// E.g. promotion to queen and white to move would result in
					// 2*(-(-1)) = 2 and for black 2*(-(1)) = -2
					case PROMOTION_QUEEN: boardArray[move.toIndex] = W_QUEEN*(-toMove); break;
					case PROMOTION_ROOK: boardArray[move.toIndex] = W_ROOK*(-toMove); break;
					case PROMOTION_BISHOP: boardArray[move.toIndex] = W_BISHOP*(-toMove); break;
					case PROMOTION_KNIGHT: boardArray[move.toIndex] = W_KNIGHT*(-toMove); break;
				}
				boardArray[move.fromIndex] = EMPTY_SQUARE; // Empty the square it moved from				
			}
		}
		
		// We now check for castling right changes, some of them are handles above, but
		// we need to catch them for both pawn promotions and ordinary moves, so we
		// have it outside the switch.

		// If neither white nor black can castle, we don't need to check for castling changes
		// and can skip the checks
		if(white_castle != CASTLE_NONE && black_castle != CASTLE_NONE)
		{
			// Now check the corners and king squares, if a piece (rook or king) is 'missing'
			// on one of those squares, we can remove the castling rights accordingly.
			//		
			// If the move is Ra2-a1 for black, in the check below it will look like
			// all is in order for castling, so the castling right won't be removed, BUT that
			// castling right should already have been removed when the rook moved to a2 in the
			// first place so we don't need to worry about that.

			if(boardArray[4] != W_KING) // White king missing from 'e1'
				white_castle = CASTLE_NONE; // Remove all castling rights for white
			if(boardArray[116] != B_KING) // Black king missing from 'e8'
				black_castle = CASTLE_NONE; // Remove all castling rights for black

			if(boardArray[0] != W_ROOK) // White rook missing from 'a1'
			{
				if(white_castle == CASTLE_BOTH) // Remove the ability to castle long
					white_castle = CASTLE_SHORT;
				else // Either only long, or none. So we set it to none
					white_castle = CASTLE_NONE;
			}

			if(boardArray[7] != W_ROOK) // White rook missing from 'h1'
			{
				if(white_castle == CASTLE_BOTH) // Remove the ability to castle short
					white_castle = CASTLE_LONG;
				else // Either only short, or none. So we set it to none
					white_castle = CASTLE_NONE;
			}

			if(boardArray[112] != B_ROOK) // Black rook missing from 'a8'
			{
				if(black_castle == CASTLE_BOTH) // Remove the ability to castle long
					black_castle = CASTLE_SHORT;
				else // Either only long, or none. So we set it to none
					black_castle = CASTLE_NONE;
			}

			if(boardArray[119] != B_ROOK) // White rook missing from 'h1'
			{
				if(black_castle == CASTLE_BOTH) // Remove the ability to castle short
					black_castle = CASTLE_LONG;
				else // Either only short, or none. So we set it to none
					black_castle = CASTLE_NONE;
			}
		}		
	}
	// END makeMove()
	
	/**
	 *  Unmakes a Move on the board
	 *
	 *  @param Move move
	 */
	public void unmakeMove(Move move)
	{
	}
	// END unmakeMove()

	/**
	 *  Returns the current position in FEN-notation
	 *
	 *  @return A string with FEN-notation
	 */
	public String getFEN()
	{
		String fen_string = ""; // This holds the FEN-string

		// ***
		// The following lines adds the pieces and empty squares to the FEN
		// ***

		int index = 112; // Keeps track of the index on the board
		int empties = 0; // Number of empty squares in a row

		while (index >= 0) // Run until end of the real board
		{
			if((index & 0x88) != 0) // Reached the end of a rank
			{
				if(empties != 0)
				{
					fen_string += empties; // Add the empties number if it's not 0
					empties = 0;
				}
				index -= 24; // Jump to the next rank
				if(index >= 0) fen_string += "/"; // Add to mark a new rank, if we're not at the end
			}	      
			else // The index is on the real board
			{
				if(boardArray[index] != EMPTY_SQUARE) // If a piece is on the square
					// i.e. the square it not empty
				{
					if(empties != 0) fen_string += empties; // Add the empty square number
					// if it's not 0
					empties = 0; // Reset empties (since we now have a piece coming)
				}

				switch(boardArray[index])
				{
					// Add the piece on the square
					case W_KING: fen_string += "K";	break;
					case W_QUEEN: fen_string += "Q"; break;
					case W_ROOK: fen_string += "R";	break;
					case W_BISHOP: fen_string += "B"; break;
					case W_KNIGHT: fen_string += "N"; break;
					case W_PAWN: fen_string += "P"; break;
					case B_KING: fen_string += "k";	break;
					case B_QUEEN: fen_string += "q"; break;
					case B_ROOK: fen_string += "r"; break;
					case B_BISHOP: fen_string += "b"; break;
					case B_KNIGHT: fen_string += "n"; break;
					case B_PAWN: fen_string += "p";	break;
					default: empties++; // If no piece, increment the empty square count
				}
				index++; // Go to the next square
			}


		}

		// END Adding pieces
		
		fen_string += " "; // Add space for next part

		// Adds side to move (important space before the letter here)
		if(toMove == WHITE_TO_MOVE) fen_string += "w"; // White's move
		else fen_string += "b"; // Black's move

		fen_string += " "; // Add space for next part

		// Castling rights
		if(white_castle == CASTLE_NONE && black_castle == CASTLE_NONE) fen_string += "-"; // Neither
		else // Atleast one side can castle one way
		{
			switch(white_castle) // Check white's castling rights
			{
				case CASTLE_SHORT: fen_string += "K"; break;
				case CASTLE_LONG: fen_string += "Q"; break;
				case CASTLE_BOTH: fen_string += "KQ"; break;
			}

			switch(black_castle) // Check black's castling rights
			{
				case CASTLE_SHORT: fen_string += "k"; break;
				case CASTLE_LONG: fen_string += "q"; break;
				case CASTLE_BOTH: fen_string += "kq"; break;
			}
		}

		fen_string += " "; // Add space for next part

		// En passant square
		
		if(enPassant == -1) fen_string += "-"; // If no en passant is available
		else // An en passant is available
		{
			switch(enPassant%16) // Find the row
			{
				case 0: fen_string += "a"; break;
				case 1: fen_string += "b"; break;
				case 2: fen_string += "c"; break;
				case 3: fen_string += "d"; break;
				case 4: fen_string += "e"; break;
				case 5: fen_string += "f"; break;
				case 6: fen_string += "g"; break;
				case 7: fen_string += "h"; break;
				default: fen_string += "Error in ep square";
			}
			switch((enPassant-(enPassant%16))/16) // Find the rank
			{
				case 2: fen_string += "3"; break;
				case 5: fen_string += "6"; break;
				default: fen_string += "Error in ep square"; // Since en passants only can occur
					 				     // on 3rd and 6th rank, any other
									     // rank is an error
			}
		}

		fen_string += " "; // Add space for next part
		fen_string += movesFifty; // Add half-moves since last capture/pawn move
		fen_string += " ";
		fen_string += movesFull; // Add number of full moves in the game so far

		return fen_string; // Returns the finished FEN-string
	}
	// END getFEN()

	/**
	 *  Takes a FEN-string and sets the board accordingly
	 *
	 * @param String fen
	 */
	public void inputFEN(String fen)
	{
		String trimmedFen = fen.trim(); // Removes any white spaces in front or behind the string
		boardArray = new int[128]; // Empties the board from any pieces
		
		String currentChar; // Holds the current character in the fen

		int i = 0; // Used to go through the fen-string character by character

		int boardIndex = 112; // Keeps track of current index on the board (while adding pieces)
				      // Starts at "a8" (index 112) since the fen string starts on this square

		int currentStep = 0; // This will be incremented when a space is detected in the string
				     // 0 - Pieces
				     // 1 - Side to move
				     // 2 - Castling rights
				     // 3 - En passant square
				     // 4 - Half-moves (for 50 move rule) and full moves

		white_castle = CASTLE_NONE; // Resetting, will be changed below if castling rights are found
		black_castle = CASTLE_NONE;
		boolean fenFinished = false; // Set to true when we're at the end of the fen-string
		while(!fenFinished && i < trimmedFen.length())
		{
			currentChar = trimmedFen.substring(i,i+1); // Gets the current character from the fen-string

			// If a space is detected, get the next character, and move to next step
			if(currentChar.equals(" ")) 
			{
				i++;
				currentChar = trimmedFen.substring(i,i+1);
				currentStep++;
			}


			switch(currentStep) // Determine what step we're on
			{
				case 0: // Pieces
				{
					switch(currentChar.charAt(0)) // See what piece is on the square
					{
						// If character is a '/' move to first row on next rank
						case '/': boardIndex -= 24; break;

						// If the character is a piece, add it and move to next square
						case 'K': boardArray[boardIndex] = W_KING; boardIndex++; break;
						case 'Q': boardArray[boardIndex] = W_QUEEN; boardIndex++; break;
						case 'R': boardArray[boardIndex] = W_ROOK; boardIndex++; break;
						case 'B': boardArray[boardIndex] = W_BISHOP; boardIndex++; break;
						case 'N': boardArray[boardIndex] = W_KNIGHT; boardIndex++; break;
						case 'P': boardArray[boardIndex] = W_PAWN; boardIndex++; break;
						case 'k': boardArray[boardIndex] = B_KING; boardIndex++; break;
						case 'q': boardArray[boardIndex] = B_QUEEN; boardIndex++; break;
						case 'r': boardArray[boardIndex] = B_ROOK; boardIndex++; break;
						case 'b': boardArray[boardIndex] = B_BISHOP; boardIndex++; break;
						case 'n': boardArray[boardIndex] = B_KNIGHT; boardIndex++; break;
						case 'p': boardArray[boardIndex] = B_PAWN; boardIndex++; break;

						// If no piece was found, it has to be a number of empty squares
						// so move to that board index
						default: boardIndex += Integer.parseInt(currentChar);
					}
				}
				case 1: // Side to move
				{
					if(currentChar.equals("w")) toMove = WHITE_TO_MOVE;
					else toMove = BLACK_TO_MOVE;
					break;
				}
				case 2: // Castling rights
				{
					// '-' states that no castling is available so we simply keep the values
					// we set before the while-loop, and don't need to do anything here.
					switch(currentChar.charAt(0))
					{
						// White can atleast castle short
						case 'K': white_castle = CASTLE_SHORT; break;

						case 'Q': // White can atleast castle long
						{
							// If white already can castle short, do both, else only long
							if(white_castle == CASTLE_SHORT) white_castle = CASTLE_BOTH;
							else white_castle = CASTLE_LONG;
							break;
						}
						// Black can atleast castle short
						case 'k': black_castle = CASTLE_SHORT; break;

						case 'q': // Black can atleast castle long
						{
							// If black already can castle short, do both, else only long
							if(black_castle == CASTLE_SHORT) black_castle = CASTLE_BOTH;
							else black_castle = CASTLE_LONG;
							break;
						}
					}
					break;					
				}
				case 3: // En passant
				{
					if(currentChar.equals("-")) enPassant = -1;
					else
					{
						switch(currentChar.charAt(0)) // Find the row
						{
							case 'a': enPassant = 0; break;
							case 'b': enPassant = 1; break;
							case 'c': enPassant = 2; break;
							case 'd': enPassant = 3; break;
							case 'e': enPassant = 4; break;
							case 'f': enPassant = 5; break;
							case 'g': enPassant = 6; break;
							case 'h': enPassant = 7; break;
						}
						// Get the next character (the rank)
						i++;
						currentChar = trimmedFen.substring(i,i+1);

						// On rank 3 or else rank 6
						if(currentChar.equals("3")) enPassant += 32; // Add 2 ranks to index
						else enPassant += 80; // Add 5 ranks to index
					}
					break;
				}
				case 4: // Half-moves (50 move rule) and full moves
				{
					// If the next character is a space, we're done with half-moves and
					// can insert them
					if(trimmedFen.substring(i+1,i+2).equals(" "))
					{
						movesFifty = Integer.parseInt(currentChar);
					}
					// If the next character is not a space, we know it's a number
					// and since half-moves can't be higher than 50 (or it can, but the game
					// is drawn so there's not much point to it), we can assume
					// there are two numbers and then we're done with half-moves.
					else
					{
						movesFifty = Integer.parseInt(trimmedFen.substring(i,i+2));
						i++;
					}
					i += 2;
					movesFull = Integer.parseInt(trimmedFen.substring(i));
					fenFinished = true; // We're done with the fen-string and can exit the loop
					break;
				}
			}
			i++; // Move to the next character in the fen-string
		}		
	}
	// END inputFEN()
	
}
